package club.westudy.distributed.lock.aop;

import club.westudy.distributed.lock.DistributedLockService;
import club.westudy.distributed.lock.annotation.DistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Order
@Slf4j
public class DistributedLockAspect {
    static Map<String, Expression> EXPRESSIONS = new HashMap<>();
    @Autowired
    DistributedLockService distributedLockService;

    @Pointcut("@annotation(club.westudy.distributed.lock.annotation.DistributedLock)")
    public void pointCut() {
    }

    @Around("pointCut() && @annotation(distributedLockAnn)")
    public Object doAround(ProceedingJoinPoint pjp, DistributedLock distributedLockAnn) throws Throwable {
        //构建一个返回值包装类，用于传入匿名内部类中
        final ReturnValueWrapper returnValueWrapper = new ReturnValueWrapper();
        Signature signature = pjp.getSignature();
        // 当切面不是方法签名时直接调用原代码
        if (!(signature instanceof MethodSignature)){
            return pjp.proceed();
        }
        // 方法切面时
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        Parameter[] parameters = method.getParameters();
        // 欲加锁特征字符串，此时可能包含有表达式
        String lockFeature = distributedLockAnn.feature();
        // 切面方法的参数数组
        Object[] args = pjp.getArgs();
        // 加锁特征字符串为空时则直接调用原代码
        if (lockFeature == null || lockFeature.isEmpty()) {
            return pjp.proceed();
        }
        // 将加锁特征字符串进行渲染
        String renderLockFeature = renderLockFeatures(method, lockFeature, parameters, args);
        // 以临界区方式执行
        executeCriticalRegion(distributedLockAnn, renderLockFeature, ctx -> returnValueWrapper.value = pjp.proceed());
        return pjp.proceed();
    }

    void executeCriticalRegion(DistributedLock distributedLockAnn, String renderLockFeature, DistributedLockService.CriticalRegionRunnable runnable) {
        //执行临界区上下文
        if(distributedLockService.execute(renderLockFeature,
                runnable,
                distributedLockAnn.maxLeaseTimeMs(),
                distributedLockAnn.tryLockWaitTimeMs())){

        }else{

        }
    }

    /**
     * 渲染锁特征字符串
     *
     * @param method
     * @param feature
     * @param parameters
     * @param args
     * @return
     */
    String renderLockFeatures(Method method, String feature, Parameter[] parameters, Object[] args) {
        Expression expression = EXPRESSIONS.get(feature);
        if (expression == null) {
            synchronized (DistributedLockAspect.class) {
                if (expression == null) {
                    ExpressionParser parser = new SpelExpressionParser();
                    expression = parser.parseExpression(feature);
                }
            }
        }
        EvaluationContext ctx = new StandardEvaluationContext();
        for (int i = 0; i < parameters.length; i++) {
            ctx.setVariable(parameters[i].getName(), args[i]);
        }
        Object value = expression.getValue(ctx);
        return value != null ? value.toString() : null;
    }
}
